using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Net;

using Google.GData.Apps;
using Google.GData.Client;
using Google.GData.Documents;
using Google.Documents;

// These methods require includes for 
// using Google.GData.Documents;


namespace GDocBackupLib
{
    public class DomainDocumentRetriever : DocumentRetriever
    {
        #region Private Variables
        private string _domain;
        private string _userName;
        private string _password;
        private string _oAuthKey;
        private Hashtable _documentRequestsByUser = new Hashtable();
        #endregion

        public DomainDocumentRetriever(string domain, string userName, string password, string oAuthkey)
        {
            _domain = domain;
            _userName = AppendDomainIfNeeded(userName, domain);
            _password = password;
            _oAuthKey = oAuthkey;
        }

        protected override RequestSettings GetRequestSettings()
        {
            if (_userName == null || _password == null || _domain == null || _oAuthKey == null)
            {
                throw new Exception("Username, password, domain and OAuthKey must be specified");
            }

            return new RequestSettings("GDocBackup-NEW2", _domain, _oAuthKey, _userName, _domain);
        }

        public override List<Document> GetFoldersForUser(string username)
        {
            DocumentsRequest docRequest = GetDocumentsRequestForUser(username);
            return ConvertFeedToList(docRequest.GetFolders());
        }

        public override List<Document> GetDocumentsForUser(string username)
        {
            List<Document> allDocs = null;

            try
            {
                // A few different tests to see if I can get anything working.
                allDocs = GetDocumentsForUserTest1(username);
                //allDocs = GetDocumentsForUserTest2(username);
                //allDocs = GetDocumentsForUserTest3(username);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return allDocs;
        }

        /// <summary>
        /// This test uses the GetEverything function, and fails :(
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        private List<Document> GetDocumentsForUserTest1(string username)
        {
            RequestSettings settings = new RequestSettings("GDocBackup-NEW2", _domain, _oAuthKey, username, _domain);
            settings.AutoPaging = true;
            settings.PageSize = 100;
            settings.Maximum = 10000;
            DocumentsRequest docRequest = new DocumentsRequest(settings);
            
            List<Document> allDocs = new List<Document>();
            Feed<Document> docs = docRequest.GetEverything();

            docs.AutoPaging = true;  // <--- try this

            foreach (Document entry in docs.Entries)
            {
                allDocs.Add(entry);
            }
            /* Line above throws exception after the first 100 items are added to the list
             * 
             * MESSAGE:
             * Execution of request failed: http://docs.google.com/feeds/default/private/full?max-results=5&start-key=EAIaxQELEoYBgAAANeSyznoyMzE0NzAxOTAyMDIuNy40ODNmYTUyZS0wMDI2LTQ3MmYtOTJkZi04OGY0YWYzMWZmM2YAc3VwZXJzaXRlIOKAkyB0ZXN0aW5nIGRhdGEgY2FsY3VsYXRpb25zADQyODQ3ODQxNTE3NS4xLmRjOXhtM3p2XzZncHFjM3dnNwAaOIAAADXkss56ASNBRQAC__t5wwICN080Mjg0Nzg0MTUxNzUuMS5kYzl4bTN6dl82Z3BxYzN3ZzcADCILCXrOsuQ1AAAAIAY&showfolders=true&xoauth_requestor_id=sgoertzen@predictiveservice.com
             * 
             * STACK TRACE:
             *       at Google.GData.Client.GDataRequest.Execute()
             *       at Google.GData.Client.GDataGAuthRequest.Execute(Int32 retryCounter)
             *       at Google.GData.Client.GDataGAuthRequest.Execute()
             *       at Google.GData.Client.Service.Query(Uri queryUri, DateTime ifModifiedSince, String etag, Int64& contentLength)
             *       at Google.GData.Client.Service.Query(Uri queryUri, DateTime ifModifiedSince)
             *       at Google.GData.Client.Service.Query(FeedQuery feedQuery)
             *       at Google.GData.Client.Feed`1.<get_Entries>d__0.MoveNext()
             *       at GDocBackupLib.DomainDocumentRetriever.GetDocumentsForUserTest1(String username) in C:\Code\svn\GDocBackup\GDocBackupLib\DocumentRetriever\DomainDocumentRetriever.cs:line 78
             *       at GDocBackupLib.DomainDocumentRetriever.GetDocumentsForUser(String username) in C:\Code\svn\GDocBackup\GDocBackupLib\DocumentRetriever\DomainDocumentRetriever.cs:line 56
             */
            return allDocs;
        }

        /// <summary>
        /// This test tries to use the new OAuth2LeggedAuthenticator class
        /// It seems this really isn't supported yet though, found almost no documentation on how to use it.
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        private List<Document> GetDocumentsForUserTest2(string username)
        {
            List<Document> allDocs = new List<Document>();
            OAuth2LeggedAuthenticator authenticator = new OAuth2LeggedAuthenticator("GDocBackup-v2", _domain, _oAuthKey, username, _domain);
            HttpWebRequest request = authenticator.CreateHttpWebRequest("Get", new Uri("https://docs.google.com/feeds/default/private/full"));
            HttpWebResponse response = request.GetResponse() as HttpWebResponse;
            /* Line above throws exception
             * 
             * MESSAGE:
             * The remote server returned an error: (400) Bad Request.
             * 
             * STACK TRACE:
             *    at System.Net.HttpWebRequest.GetResponse()
             *    at GDocBackupLib.DomainDocumentRetriever.GetDocumentsForUserTest2(String username) in C:\Code\svn\GDocBackup\GDocBackupLib\DocumentRetriever\DomainDocumentRetriever.cs:line 108
             *    at GDocBackupLib.DomainDocumentRetriever.GetDocumentsForUser(String username) in C:\Code\svn\GDocBackup\GDocBackupLib\DocumentRetriever\DomainDocumentRetriever.cs:line 59
             */

            return allDocs;
        }

        private List<Document> GetDocumentsForUserTest3(string username)
        {
            List<Document> allDocs = new List<Document>();
            GOAuthRequestFactory requestFactory = new GOAuthRequestFactory("DocRetreiver", "GDocBackup-v2");
            requestFactory.ConsumerKey = _domain;
            requestFactory.ConsumerSecret = _oAuthKey;

            DocumentsService client = new DocumentsService("GDocBackup-v2");
            client.RequestFactory = requestFactory;
            DocumentsListQuery query = new DocumentsListQuery();
            int count = 0;
            query.Uri = new OAuthUri("https://docs.google.com/feeds/default/private/full", username, _domain);
            query.ShowFolders = false;
            query.NumberToRetrieve = 1000;
            DocumentsFeed feed = client.Query(query);
            // This only returns 100 rows then stops
            foreach (DocumentEntry entry in feed.Entries)
            {
                Console.WriteLine(entry.Title.Text);
                // TODO: Would still need to convert DocumentEntry to Document somehow...
                count++;
            }

            // Try getting the second set of 100 by setting the start index to 100
            // This doesn't work and returns the original 100 again instead
            /*query.Uri = new OAuthUri("https://docs.google.com/feeds/default/private/full", username, _domain);
            query.ShowFolders = false;
            query.NumberToRetrieve = 1000;
            query.StartIndex = 100;
            DocumentsFeed feed3 = client.Query(query);
            // This returns the first 100 again  :(
            foreach (DocumentEntry entry in feed3.Entries)
            {
                Console.WriteLine(entry.Title.Text);
                // TODO: Would still need to convert DocumentEntry to Document somehow...
                count++;
            }*/

            // Try getting the second set of 100 by using the next chunk URL
            // This also fails and returns the original 100 again
            string nextFeedURL = feed.NextChunk;
            query.Uri = new OAuthUri(nextFeedURL, username, _domain);
            query.ShowFolders = false;
            query.NumberToRetrieve = 1000;
            DocumentsFeed feed4 = client.Query(query);
            // This returns the first 100 again  :(
            foreach (DocumentEntry entry in feed4.Entries)
            {
                Console.WriteLine(entry.Title.Text);
                // TODO: Would still need to convert DocumentEntry to Document somehow...
                count++;
            }

            return allDocs;
        }

        public override DocumentsRequest GetDocumentsRequestForUser(string username)
        {
            DocumentsRequest docReq = (DocumentsRequest)_documentRequestsByUser[username];
            if (docReq == null)
            {
                // Get doc list from GDocs
                RequestSettings settings = new RequestSettings("GDocBackup-NEW2", _domain, _oAuthKey, username, _domain);
                settings.AutoPaging = AUTO_PAGING;
                settings.PageSize = PAGE_SIZE;
                docReq = new DocumentsRequest(settings);
                _documentRequestsByUser.Add(username, docReq);
            }
            return docReq;
        }

        public override string[] GetUsers()
        {
            return new string[] { "sgoertzen" };
            /*
            // This feed requires the username to be fully qualified with the domain
            AppsService appsServ = new AppsService(_domain, _userName, _password);
            UserQuery uQuery = new UserQuery(_domain, true);

            ArrayList usernames = new ArrayList();
            try
            {
                UserFeed usersFeed = appsServ.RetrieveAllUsers();
                foreach (UserEntry entry in usersFeed.Entries)
                {
                    usernames.Add(entry.Login.UserName);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Unable to retrieve a list of users.", e);
            }
            return (string[])usernames.ToArray(typeof(String));*/
        }

        private string AppendDomainIfNeeded(string username, string domain)
        {
            if (!username.ToLower().EndsWith(domain.ToLower()))
            {
                username = username + "@" + domain;
            }
            return username;
        }
    }
}
